#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/kdev_t.h>

MODULE_LICENSE("GPL");

#define MODULENAME "character"
#define MINOR_NUM_COUNT 1 // 要申请的设备号个数。主设备号是同样的，次设备号不同

#define LOG_INFO(fmt, ...) \
    printk(KERN_INFO MODULENAME ": " fmt, ##__VA_ARGS__)

#define LOG_ERR(fmt, ...) \
    printk(KERN_ERR MODULENAME ": " fmt, ##__VA_ARGS__)

static int major_num, minor_num;

module_param(major_num, int, S_IRUSR);
module_param(minor_num, int, S_IRUSR);


static int __init character_init(void)
{
    LOG_INFO("Hello, world\n");
    if(major_num != 0)
    {//静态分配
        // 传入的参数分别是设备号，次设备号个数，设备名称
        int ret = register_chrdev_region(MKDEV(major_num, minor_num), MINOR_NUM_COUNT, "character");
        if (ret < 0)
        {
            LOG_ERR("register_chrdev_region failed\n");
            return ret;
        }
        else
        {
            LOG_INFO("register_chrdev_region success\n");
            LOG_INFO("major_num = %d, minor_num = %d\n", major_num, minor_num);
        }
    }
    else
    {//动态分配
        dev_t dev;
        // 传入的参数分别是 需要保存的设备号的地址，次设备号起始值，次设备号个数，设备名称
        int ret = alloc_chrdev_region(&dev, 0, MINOR_NUM_COUNT, "dynamiccharacter");
        if (ret < 0)
        {
            LOG_ERR("alloc_chrdev_region failed\n");
        }
        else
        {
            major_num = MAJOR(dev);
            minor_num = MINOR(dev);
            LOG_INFO("alloc_chrdev_region success\n");
            LOG_INFO("major_num = %d, minor_num = %d\n", MAJOR(dev), MINOR(dev));
        }
    }
    return 0;
}

static void character_exit(void)
{
    // 注销设备号
    // 传入的参数分别是设备号，次设备号个数
    unregister_chrdev_region(MKDEV(major_num, minor_num), MINOR_NUM_COUNT);
    LOG_INFO("Goodbye, world\n");
}

module_init(character_init);
module_exit(character_exit);
